package org.iabako.client.ui.form;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiTemplate;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.*;
import com.google.gwt.validation.client.impl.Validation;
import org.iabako.client.config.layout.animation.IabakoAnimation;
import org.iabako.client.config.page.PageConfigEnum;
import org.iabako.client.config.page.PageEnum;
import org.iabako.client.navigation.NavigationController;
import org.iabako.client.tools.Messages;
import org.iabako.client.ui.layout.field.IField;
import org.iabako.client.ui.layout.field.InputTextSuggest;
import org.iabako.client.ui.layout.field.LabelText;
import org.iabako.client.ui.layout.field.SelectList;
import org.iabako.client.ui.layout.template.Popup;
import org.iabako.client.ui.layout.widget.tooltip.TooltipListener;
import org.iabako.client.ui.layout.widget.validation.ValidationMessage;
import org.iabako.shared.entity.business.AbstractEntity;
import org.iabako.shared.entity.business.BusinessEntity;
import org.iabako.shared.iservice.generic.BusinessEntityServiceAsync;
import org.iabako.shared.tools.GenericTools;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

/**
 * TODO javadoc
 * Date: 24/11/13
 * Time: 18:22
 */
public abstract class AbstractForm<E extends AbstractEntity> extends Composite {

    protected Logger log = Logger.getLogger(this.getClass().getName());
    public static final MyUiBinderPopupTemplate binderPopup = GWT.create(MyUiBinderPopupTemplate.class);
    public Popup popup;

    @UiField
    public ValidationMessage messages;

    public AbstractEntity entity;

    protected AbstractForm parentForm;

    protected PageConfigEnum pageConfigEnum;

    protected boolean simplePopup = false;

    public BusinessEntityServiceAsync businessEntityService;

    @UiTemplate("org.iabako.client.ui.layout.template.Popup.ui.xml")
    interface MyUiBinderPopupTemplate extends UiBinder<HTMLPanel, Popup> {
    }

    protected AbstractForm() {
    }

    protected void setForm(UiBinder<HTMLPanel, AbstractForm> binder, PageConfigEnum pageConfigEnum, String explanationMenuLabel) {
        this.pageConfigEnum = pageConfigEnum;

        popup = new Popup();
        HTMLPanel popupPanel = binderPopup.createAndBindUi(popup);

        HTMLPanel popupContentPanel = binder.createAndBindUi(this);

        popup.setSimplePopup(simplePopup);
        popup.setProperties(pageConfigEnum, explanationMenuLabel, this);
        popup.addToPopupContent(popupContentPanel);

        initWidget(popupPanel);
    }

    @Override
    protected void onLoad() {
        IabakoAnimation iabakoAnimation = new IabakoAnimation(Popup.mainLayoutDiv);
        int popupHeight = Popup.mainLayoutDiv.getClientHeight();
        int screenHeight = RootPanel.get().getElement().getClientHeight();
        if (simplePopup && popupHeight < screenHeight && !Popup.mainLayoutDiv.getClassName().contains("mediumpopup")) {
            Popup.mainLayoutDiv.getStyle().setMarginTop(-1 * (Popup.mainLayoutDiv.getOffsetHeight()), Style.Unit.PX);
            iabakoAnimation.slideFromTopToCenter(300);
        } else {
            Popup.mainLayoutDiv.setAttribute("style", Popup.mainLayoutDiv.getAttribute("style") + "; margin-top: 70px");
            iabakoAnimation.slideFromMarginTop(300, -1 * (Popup.mainLayoutDiv.getOffsetHeight()));
        }
    }

    public void enableAddLinkAction(BusinessEntityServiceAsync businessEntityServiceAsync) {
        if (entity == null || entity.getId() == null || !(entity instanceof BusinessEntity)) {
            return;
        }

        this.businessEntityService = businessEntityServiceAsync;

        String tooltipMessage = ((BusinessEntity) entity).getExternalLinkName() == null ?
                "add_link_to_business_object" : "modify_link_to_business_object";

        SimplePanel simplePanel = new SimplePanel();
        Element addLinkElement = DOM.createDiv();
        addLinkElement.setClassName("addlinkaction");
        DOM.sinkEvents(addLinkElement, Event.MOUSEEVENTS);
        DOM.setEventListener(addLinkElement, new TooltipListener(Messages.getLabel(tooltipMessage)));
        simplePanel.getElement().insertFirst(addLinkElement);

        DOM.sinkEvents(simplePanel.getElement(), Event.ONCLICK);

        final AbstractForm thisForm = this;
        simplePanel.addHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                NavigationController.getInstance().goTo(PageEnum.AddLinkToBusinessObject, thisForm);
            }
        }, ClickEvent.getType());

        popup.footerCustomActionPanel.add(simplePanel);

        refreshLinkPanel();
    }

    public void refreshLinkPanel() {
        popup.externalLinkPanel.clear();

        if (entity instanceof BusinessEntity
                && !(GenericTools.isEmpty(((BusinessEntity) entity).getExternalLinkName()))
                && !(GenericTools.isEmpty(((BusinessEntity) entity).getExternalLinkUrl()))) {

            LabelText labelText = new LabelText("");
            labelText.setStyleClass("linklabel externalbusinesslink");
            labelText.setLabel("+ " + ((BusinessEntity) entity).getExternalLinkName());

            labelText.getLabel().addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    Window.open(((BusinessEntity) entity).getExternalLinkUrl(), ((BusinessEntity) entity).getExternalLinkName(), "");
                }
            });

            popup.externalLinkPanel.add(labelText);
        }
    }

    public void addToFooterCustomActionPanel(Widget widget) {
        popup.footerCustomActionPanel.add(widget);
    }

    public void addToActionCustomButtons(Widget widget) {
        popup.actionCustomButtons.add(widget);
    }

    public boolean uiValidation() {
        ValidationMessage validationMessage = ValidationMessage.getFirstFromDOM();
        if (validationMessage.isError()) {
            validationMessage.showErrorMessage(true, true, "validation_form_ui_error");
            return false;
        }
        List<IField> invalidField = getInvalidMandatoryFields();
        if (!invalidField.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            for (IField field : invalidField) {
                sb.append(field.getTitle() != null && !field.getTitle().isEmpty() ? field.getTitle().replace(":", "").trim() + ", " : "");
            }
            //Delete last comma or space
            String invalidFields = sb.toString().length() > 1 ? sb.toString().substring(0, sb.toString().length() - 2) : "";
            validationMessage.showErrorMessage(true, true, Messages.getLabel("validation_form_ui_mandatory_error") + "<br/>---> " + invalidFields + ".");

            validationMessage.getElement().scrollIntoView();

            return false;
        }
        return true;
    }

    protected List<IField> getInvalidMandatoryFields() {
        List<IField> invalidMandatoryFields = new ArrayList<IField>();
        for (IField field : getMandatoryFields()) {

            if (GenericTools.isEmpty(getFieldValue(field))) {
                invalidMandatoryFields.add(field);
            }
        }
        return invalidMandatoryFields;
    }

    private Object getFieldValue(IField field) {
        if (field instanceof SelectList) {
            return ((SelectList) field).getEnumValue() != null ? ((SelectList) field).getEnumValue() : field.getValue();
        } else if (field instanceof InputTextSuggest) {
            return ((InputTextSuggest) field).getHiddenSelectionCode().getValue();
        }
        return field.getValue();
    }

    public E serverValidation() {

        ValidationMessage validationMessage = ValidationMessage.getFirstFromDOM();

        Set<String> functionalServerErrors = new HashSet<String>();

        ValidatorFactory factory = Validation.byDefaultProvider().configure().buildValidatorFactory();
        Validator validator = factory.getValidator();

        E entity = getEntity();

        Set<ConstraintViolation<E>> violations = validator.validate(entity);

        if (!violations.isEmpty()) {
            for (ConstraintViolation<E> c : violations) {
                if (c instanceof org.hibernate.validator.constraints.impl.NotNullValidator) {
                    functionalServerErrors.add("validation_form_mandatory_fields_error");
                } else {
                    functionalServerErrors.add(c.getMessage());
                }
            }
        }

        if (!functionalServerErrors.isEmpty()) {
            validationMessage.showErrorMessage(true, true, functionalServerErrors.toArray(new String[functionalServerErrors.size()]));
            return null;
        }

        return entity;
    }

    public void initialize() {
        //To be override
    }

    public boolean isSimplePopup() {
        return simplePopup;
    }

    public void setParentForm(AbstractForm parentForm) {
        this.parentForm = parentForm;
    }

    abstract protected List<IField> getMandatoryFields();

    abstract protected E getEntity();

    abstract public boolean validate();
}
